#include "mpFileUtils.h"
#include "mpLocaleUtils.h"
#include "mpDateUtils.h"
#include "mpLog.h"

#ifdef PLATFORM_WINDOWS
#include "mpWin32Func.h"
#endif //PLATFORM_WINDOWS

#ifdef PLATFORM_UNIX
#include <fcntl.h>
#endif  //PLATFORM_UNIX

#include <boost/filesystem.hpp>

#if defined(PLATFORM_UNIX)
#include <unistd.h>
#include <sys/stat.h>
#ifdef HAVE_ST_BIRTHTIME
#define birthtime(x) x.st_birthtime
#else
#define birthtime(x) x.st_ctime
#endif
#endif

namespace MP{
namespace FileUtils{

////private member
//namespace{
//} //private member


std::string get_appdir(){
    static std::string path;

#ifdef PLATFORM_WINDOWS
    if(path.empty()){
        char szPath[MAX_PATH + 1] = {0};
        if(GetModuleFileNameA(NULL, szPath, MAX_PATH)){
            boost::filesystem::path exePath(szPath);
            path = exePath.remove_filename().string();
        }

        MPLOG_INFO << "app dir path : " << path;
    }
#endif

    if(path.empty()){
        path = boost::filesystem::initial_path<boost::filesystem::path>().string();
    }
    return path;
}

std::string get_current_userdata_dir(){
    std::string current_userdata_dir;

#if defined(PLATFORM_UNIX)
#if defined(MAPLE_MODE_DEBUGGING)
    current_userdata_dir = get_appdir() + "/workdir/current_userdata";
#else
    current_userdata_dir = "/opt/apps/" + std::string(WORK_DIR_NAME) + "/files/workdir/current_userdata";
#endif //MAPLE_MODE_DEBUGGING
#endif //PLATFORM_UNIX

#if defined(PLATFORM_WINDOWS)

    std::wstring wstr;
    if(MP::Win32Func::mpsGetCurrentLogonUserLocalPath(wstr)){
        //转成本地系统编码的string
        std::string strPath = MP::LocaleUtils::unicode_to_sys_encoding(wstr);
        //检查文件是否存在
        if (check_dir_isexist(strPath)){
            //判断shuzibao文件夹是否存在，如果不存在则创建之
            current_userdata_dir = strPath + PATH_DELIM + WORK_DIR_NAME;
        }
    }

//    std::wstring wstr;
//    if(MP::Win32Func::mpsGetDefaultUserLocalPath(wstr)){

//        //转成本地系统编码的string
//        std::string strPath = MP::LocaleUtils::unicode_to_sys_encoding(wstr);

//        //检查文件是否存在
//        if (check_dir_isexist(strPath)){
//            //判断shuzibao文件夹是否存在，如果不存在则创建之
//            current_userdata_dir = strPath + PATH_DELIM + WORK_DIR_NAME;
//        }
//    }

#endif //PLATFORM_WINDOWS

    //检查文件夹是否存在，不存在则创建，创建失败则清空字符串
    if (!check_dir_isexist(current_userdata_dir))
    {
        if(create_directories(current_userdata_dir)){
            current_userdata_dir.clear();
        }
    }


    if(current_userdata_dir.empty()){
        //目录不存在则返回默认值
        return get_appdir();
    }
    else{
        return current_userdata_dir;
    }
}

std::string get_all_userdata_dir(){
    std::string all_userdata_dir;

#if defined(PLATFORM_UNIX)
#if defined(MAPLE_MODE_DEBUGGING)
    all_userdata_dir = get_appdir() + "/workdir/all_userdata";
#else
    all_userdata_dir = "/opt/apps/" + std::string(WORK_DIR_NAME) + "/files/workdir/all_userdata";
#endif //MAPLE_MODE_DEBUGGING
#endif //PLATFORM_UNIX

#if defined(PLATFORM_WINDOWS)
    std::wstring wstr;
    if(MP::Win32Func::mpsGetDefaultAllUserLocalPath(wstr)){

        //转成本地系统编码的string
        std::string strPath = MP::LocaleUtils::unicode_to_sys_encoding(wstr);

        //检查文件是否存在
        if (check_dir_isexist(strPath)){
            //判断shuzibao文件夹是否存在，如果不存在则创建之
            all_userdata_dir = strPath + PATH_DELIM + WORK_DIR_NAME;
        }
    }
#endif //PLATFORM_WINDOWS

    //检查文件夹是否存在，不存在则创建，创建失败则清空字符串
    if (!check_dir_isexist(all_userdata_dir))
    {
        if(!create_directories(all_userdata_dir)){
            all_userdata_dir.clear();
        }
    }

    if(all_userdata_dir.empty()){
        return get_appdir();        //目录不存在则返回默认值
    }
    else{
        return all_userdata_dir;
    }
}

std::string get_desktopdir(){

    std::string desktopdir;

#if defined(PLATFORM_UNIX)
    //先实现Linux桌面的路径
    char* pHOME;
    pHOME = getenv("HOME");
    if (pHOME!=NULL){
        std::string homeDir = std::string(pHOME) + "/桌面";
        if (check_dir_isexist(homeDir)){
            desktopdir = homeDir;
        }else{
            homeDir = std::string(pHOME) + "/文档";
            if (check_dir_isexist(homeDir)){
                desktopdir = homeDir;
            }else{
                homeDir = std::string(pHOME);
                if (check_dir_isexist(homeDir)){
                    desktopdir = homeDir;
                }
            }
        }
    }
#endif //PLATFORM_UNIX

#if defined(PLATFORM_WINDOWS)

    std::wstring wstr;
    if(MP::Win32Func::mpsGetDefaultUserDestopPath(wstr)){
        //转成本地系统编码的string
        desktopdir = MP::LocaleUtils::unicode_to_sys_encoding(wstr);
        if (!check_dir_isexist(desktopdir)){
            desktopdir.clear();
        }
    }
#endif //PLATFORM_WINDOWS

    if(desktopdir.empty()){
        return get_appdir();//目录不存在则返回默认值
    }
    else{
        return desktopdir;
    }

}

bool set_file_content(const std::string & path,const std::string & content,bool overwrite){

    //判断文件所在文件夹是否存在，如果不存在则创建.创建失败则返回false
    if(!create_directories_forfile(path)){
        MPLOG_ERROR << "could not create directory ,Path:" << path;
        return false;
    }

    //ios::app              // 以追加的方式打开文件
    //ios::ate              // 文件打开后定位到文件尾，ios:app就包含有此属性
    //ios::binary           // 以二进制方式打开文件，缺省的方式是文本方式。
    //ios::in               // 文件以输入方式打开（文件数据输入到内存）
    //ios::out              // 文件以输出方式打开（内存数据输出到文件）
    //ios::nocreate         // 不建立文件，所以文件不存在时打开失败
    //ios::noreplace        // 不覆盖文件，所以打开文件时如果文件存在失败
    //ios::trunc            // 如果文件存在，把文件长度设为0
    std::fstream file;

    if(overwrite){
        file.open(path,std::ios::out|std::ios::binary);
    }
    else{
        file.open(path,std::ios::out|std::ios::app|std::ios::binary);
    }

    if(!file){
        MPLOG_ERROR << "could not open file ,Path:" << path;
        return false;
    }

    file << content;
    file.close();

    return true;
}

bool get_file_content(const std::string & path,std::string & content){

    if(!check_file_isexist(path)){
        MPLOG_ERROR << "get file text content err, file is not exist ." << path;
        return false;
    }

    std::ifstream fin(path,std::ios::in);
    //尝试打开文件，如果失败则返回False
    if (!fin){
        MPLOG_ERROR << "get file text content error " << path;
        return false;
    }

    std::stringstream sstream;
    sstream << fin.rdbuf();
    fin.close();

    content = sstream.str();

    return true;
}

void get_file_birthtime(const std::string & path,std::string & strbirthtime){
    strbirthtime.clear();

    if(!check_file_isexist(path)){
        MPLOG_ERROR << "get file birthtime err , file is not exist ." << path;
        return;
    }

#if defined(PLATFORM_UNIX)
    struct stat st;
    if (::stat(path.c_str(), &st) == 0) {
        strbirthtime = MP::DateUtils::time_to_local_datetime_string(birthtime(st));
    }
#endif //PLATFORM_UNIX

#if defined(PLATFORM_WINDOWS)

    FILETIME ftCreate, ftAccess, ftModify;
    HANDLE hFile = CreateFileA(path.c_str(),
                               GENERIC_READ,
                               FILE_SHARE_READ,
                               NULL,
                               OPEN_EXISTING,
                               FILE_FLAG_BACKUP_SEMANTICS,
                               NULL);

    if (hFile != INVALID_HANDLE_VALUE)
    {
        if (GetFileTime(hFile, &ftCreate, &ftAccess , &ftModify)){
            strbirthtime = MP::DateUtils::filetime_to_local_datetime_string(ftCreate);
        }
    }
    else{
        MPLOG_ERROR << "get file birthtime err , call win api CreateFileA fail ." << path;
    }
    CloseHandle(hFile);

#endif //PLATFORM_WINDOWS

}

bool get_file_is_regular_file(const std::string & path){

    if(!check_file_isexist(path)){
        MPLOG_ERROR << "get file size err , file is not exist ." << path;
        return false;
    }

    return boost::filesystem::is_regular_file(path);
}

std::size_t get_file_size(const std::string & path){
    std::size_t result = 0;

    if(!get_file_is_regular_file(path)){
        MPLOG_ERROR << "get file size err , file is not regular file ." << path;
        return result;
    }

    //文件大小(字节)
    try
    {
        std::ifstream stream{ path, std::ios::binary | std::ios::ate };
        if (stream) {
            result = static_cast<std::size_t>(stream.tellg());
            stream.close();
        }
        else{
            MPLOG_ERROR << "get file size err,ifstream open fail." << path ;
        }
    }
    catch  (...)
    {
        MPLOG_ERROR << "get file size failed ." << path ;
    }

    //    // 计算文件大小
    //    file.seekg(0, ios::end);
    //    size_t size = file.tellg();
    //    file.seekg(0);

    return result;
}

std::string get_file_name(const std::string & path,
                          bool is_include_extension){

    if(!check_file_isexist(path)){
        MPLOG_ERROR << "get file name err , file is not exist ." << path;
        return "";
    }

    boost::filesystem::path filePath(path);
    if(is_include_extension){
        return filePath.filename().string();
    }
    else{
        return filePath.stem().string();
    }
}


bool remove_file(const std::string & path){

    if(!check_file_isexist(path)){
        return false;
    }

    bool result = false;
    try
    {
        result = boost::filesystem::remove(path);
    }
    catch  (...)
    {
        MPLOG_ERROR << "remove file err ." << path ;
    }

    return result;
}

bool remove_dir(const std::string & dir_path){

    if(!check_dir_isexist(dir_path)){
        return false;
    }

    bool result = false;
    try
    {
        boost::filesystem::path path_to_remove(dir_path);
        boost::filesystem::remove_all(path_to_remove);

        result = true;
    }
    catch  (...)
    {
        MPLOG_ERROR << "remove dir err ." << dir_path ;
    }

    return result;


}

bool clear_dir(const std::string & dir_path){

    if(!check_dir_isexist(dir_path)){
        MPLOG_ERROR << "clear dir err , dir is not exist ." << dir_path;
        return false;
    }

    bool result = false;
    try
    {
        boost::filesystem::path path_to_remove(dir_path);

        for (boost::filesystem::directory_iterator end_dir_it, it(path_to_remove); it!=end_dir_it; ++it) {
            boost::filesystem::remove_all(it->path());
        }

        result = true;
    }
    catch  (...)
    {
        MPLOG_ERROR << "clear dir err ." << dir_path ;
    }

    return result;




    return true;
}

bool check_dir_access(const std::string & dir_path){

    if(!check_dir_isexist(dir_path)){
        MPLOG_ERROR << "check_dir_access err , dir is not exist ." << dir_path;
        return false;
    }

    bool result = false;
    try
    {
        std::string testFile = dir_path + PATH_DELIM + "mp-access-test-file.tmp";
        if(touch_file(testFile)){
            result = remove_file(testFile);
        }
    }
    catch (...) {
        MPLOG_ERROR << "check_dir_access failed ." << dir_path;
    }
    return result;
}

bool check_file_isexist(const std::string & path){
    boost::system::error_code error;
    boost::filesystem::path boost_path(path);

    auto file_status = boost::filesystem::status(boost_path, error);
    if (error) {
        return false;
    }
    if (!boost::filesystem::exists(file_status)) {
        return false;
    }
    if (boost::filesystem::is_directory(file_status)) {
        return false;
    }
    return true;
}

bool check_dir_isexist(const std::string & dir_path){
    boost::system::error_code error;

    boost::filesystem::path boost_path(dir_path);
    auto file_status = boost::filesystem::status(boost_path, error);
    if (error) {
        return false;
    }
    if (!boost::filesystem::exists(file_status)) {
        return false;
    }
    if (!boost::filesystem::is_directory(file_status)) {
        return false;
    }
    return true;
}

bool check_isdir(const std::string& dir_path){
    return check_dir_isexist(dir_path);
}

bool touch_file(const std::string & path){
#ifdef PLATFORM_UNIX
    // see http://pubs.opengroup.org/onlinepubs/9699919799/utilities/touch.html
    //
    // we use utimes/futimes instead of utimensat/futimens for compatibility
    // with older Linux and Mac

    if (check_file_isexist(path))
    {
        utimes(path.c_str(),0 /* use current date/time */);
        return true;
    }
    else
    {
        int fd = creat(path.c_str(),S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
        if (fd != -1)
        {
            futimes(fd,0 /* use current date/time */);
            close(fd);
            return true;
        }
        else
        {
            return false;
        }
    }
#else

    HANDLE result = CreateFileA(path.c_str(),GENERIC_WRITE,
                                FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
                                0,
                                CREATE_ALWAYS,
                                FILE_ATTRIBUTE_NORMAL,
                                0);
    if (result == INVALID_HANDLE_VALUE)
    {
        return false;
    }
    else
    {
        CloseHandle(result);
        return true;
    }
#endif

}

void copy_file(const std::string& from_fp, const std::string& to_fp,bool is_overwrite){
    if(check_file_isexist(to_fp)){
        if(is_overwrite){
            boost::filesystem::copy_file(from_fp , to_fp,boost::filesystem::copy_option::overwrite_if_exists);
        }
    }else{
        boost::filesystem::copy_file(from_fp , to_fp);
    }
}

void move_file(const std::string& from_fp, const std::string& to_fp,bool is_overwrite){
    //先复制再删除
    copy_file(from_fp,to_fp,is_overwrite);
    remove_file(from_fp);
}

void rename_file(const std::string& from_fp, const std::string& to_fp){

    if(check_file_isexist(from_fp)){
        remove_file(to_fp);
        boost::filesystem::rename(from_fp,to_fp);
    }
}

bool create_directories(const std::string & dir_path){
    boost::filesystem::path boost_path(dir_path);
    return boost::filesystem::create_directories(boost_path);
}

bool create_directories_forfile(const std::string & filePath){

    boost::filesystem::path boost_path(filePath);

    boost::filesystem::path boost_dir = boost_path.remove_filename();
    if(!check_dir_isexist(boost_dir.string())){
        return boost::filesystem::create_directories(boost_dir);
    }
    return true;
}


}   //END FileUtils
}   //END MP




